home *** CD-ROM | disk | FTP | other *** search
/ The Very Best of Atari Inside / The Very Best of Atari Inside 1.iso / mint / mntlb20 / lib / localtim.c < prev    next >
C/C++ Source or Header  |  1992-05-15  |  10KB  |  334 lines

  1. /* mktime, localtime, gmtime */
  2. /* written by ERS and placed in the public domain */
  3.  
  4. #include <stddef.h>
  5. #include <stdlib.h>
  6. #include <time.h>
  7. #include <ctype.h>
  8. #ifndef _COMPILER_H
  9. #include <compiler.h>
  10. #endif
  11.  
  12. #if 0
  13. static void
  14. DEBUG_TM(nm, tm)
  15.     char *nm;
  16.     struct tm *tm;
  17. {
  18.     char buf[100];
  19.  
  20.     (void)strftime(buf, 100, "%c %z", tm);
  21.     printf("%s: %s\n", nm, buf);
  22. }
  23. #else
  24. #define DEBUG_TM(nm, tm)
  25. #endif
  26.  
  27. #define SECS_PER_MIN    (60L)
  28. #define SECS_PER_HOUR   (3600L)
  29. #define SECS_PER_DAY    (86400L)
  30. #define SECS_PER_YEAR   (31536000L)
  31. #define SECS_PER_LEAPYEAR (SECS_PER_DAY + SECS_PER_YEAR)
  32.  
  33. time_t _timezone = -1;    /* holds # seconds west of GMT */
  34.  
  35. static int
  36. days_per_mth[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
  37.  
  38. static int
  39. mth_start[13] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };
  40.  
  41. static time_t tzoffset __PROTO((char *s, int *hasdst));
  42. static int indst __PROTO((long secs, const struct tm *t));
  43.  
  44. static int dst = -1;    /* whether dst holds in current timezone */
  45.  
  46. /*
  47.  * FIXME: none of these routines is very efficient. Also, none of them
  48.  * handle dates before Jan 1, 1970.
  49.  *
  50.  */
  51.  
  52. /*
  53.  * mktime: take a time structure representing the local time (such as is
  54.  *  returned by localtime() and convert it into the standard representation
  55.  *  (as seconds since midnight Jan. 1 1970, GMT).
  56.  *
  57.  * Note that time() sends us such a structure with tm_yday and tm_wday
  58.  * undefined, so we shouldn't count on these being correct!
  59.  */
  60.  
  61. time_t
  62. mktime(t)
  63.         const struct tm *t;
  64. {
  65.         time_t s;
  66.         int y;
  67.  
  68. DEBUG_TM("mktime", t);
  69.         if (t->tm_year < 70)      /* year before 1970 */
  70.                 return (time_t) -1;
  71.  
  72. /* calculate tm_yday here */
  73.     y = (t->tm_mday - 1) + mth_start[t->tm_mon] + /* leap year correction */
  74.         ( ( (t->tm_year % 4) != 0 ) ? 0 : (t->tm_mon > 1) );
  75.  
  76.     s = (t->tm_sec)+(t->tm_min*SECS_PER_MIN)+(t->tm_hour*SECS_PER_HOUR) +
  77.         (y*SECS_PER_DAY)+((t->tm_year - 70)*SECS_PER_YEAR) +
  78.         ((t->tm_year - 69)/4)*SECS_PER_DAY;
  79.  
  80. /* Now adjust for the time zone and possible daylight savings time */
  81. /* note that we have to call tzset() every time; see 1003.1 sect 8.1.1 */
  82.     tzset();
  83.  
  84.         s += _timezone;
  85.         if (dst == 1 && indst(s, t))
  86.                 s -= SECS_PER_HOUR;
  87.  
  88.         return s;
  89. }
  90.  
  91.  
  92. struct tm *_gmtime(t, stm)
  93.         const time_t *t;
  94.     struct tm *stm;
  95. {
  96.         time_t  time = *t;
  97.         int     year, mday, i;
  98.  
  99.         if (time < 0)   /* negative times are bad */
  100.                 return 0;
  101.         stm->tm_wday = ((time/SECS_PER_DAY) + 4) % 7;
  102.  
  103.         year = 70;
  104.         for (;;) {
  105.                 if (time < SECS_PER_YEAR) break;
  106.                 if ((year % 4) == 0) {
  107.                         if (time < SECS_PER_LEAPYEAR)
  108.                                 break;
  109.                         else
  110.                                 time -= SECS_PER_LEAPYEAR;
  111.                 }
  112.                 else
  113.                         time -= SECS_PER_YEAR;
  114.                 year++;
  115.         }
  116.         stm->tm_year = year;
  117.         mday = stm->tm_yday = time/SECS_PER_DAY;
  118.  
  119.         days_per_mth[1] = (year % 4) ? 28 : 29;
  120.         for (i = 0; mday >= days_per_mth[i]; i++)
  121.                 mday -= days_per_mth[i];
  122.         stm->tm_mon = i;
  123.         stm->tm_mday = mday + 1;
  124.  
  125.         time = time % SECS_PER_DAY;
  126.         stm->tm_hour = time/SECS_PER_HOUR;
  127.         time = time % SECS_PER_HOUR;
  128.         stm->tm_min = time/SECS_PER_MIN;
  129.         stm->tm_sec = time % SECS_PER_MIN;
  130.         stm->tm_isdst = 0;
  131.  
  132. DEBUG_TM("gmtime", stm);
  133.         return stm;
  134. }
  135.  
  136. struct tm *gmtime(t)
  137.     const time_t *t;
  138. {
  139.     static struct tm gtime;
  140.  
  141.     return _gmtime(t, >ime);
  142. }
  143.  
  144. /* given a standard time, convert it to a local time */
  145.  
  146. struct tm *localtime(t)
  147.         const time_t *t;
  148. {
  149.     static struct tm ltim;
  150.         struct tm *stm;
  151.         time_t gmsecs;  /*time in GMT */ 
  152.  
  153.     tzset();
  154.         gmsecs = *t - _timezone;
  155.         stm = _gmtime(&gmsecs, <im);
  156.     if (stm == NULL) return stm;        /* check for illegal time */
  157.         stm->tm_isdst = (dst == -1) ? -1 : 0;
  158.  
  159.         if (dst == 1 && indst(*t, (const struct tm *)stm)) {
  160.        /* daylight savings time in effect */
  161.                 stm->tm_isdst = 1;
  162.                 if (++stm->tm_hour > 23) {
  163.                         stm->tm_hour -= 24;
  164.                         stm->tm_wday = (stm->tm_wday + 1) % 7;
  165.                         stm->tm_yday++;
  166.                         stm->tm_mday++;
  167.                         if (stm->tm_mday > days_per_mth[stm->tm_mon]) {
  168.                                 stm->tm_mday = 1;
  169.                                 stm->tm_mon++;
  170.                         }
  171.                 }
  172.         }
  173.     DEBUG_TM("localtime", stm);
  174.         return stm;
  175. }
  176.  
  177. /*
  178.  * THIS IS A DELIBERATE VIOLATION OF THE ANSI STANDARD:
  179.  * there appears to be a conflict between Posix and ANSI; the former
  180.  * mandates a "tzset()" function that gets called whenever time()
  181.  * does, and which sets some global variables. ANSI wants none of
  182.  * this. Several Unix implementations have tzset(), and few people are
  183.  * going to be hurt by it, so it's included here.
  184.  */
  185.  
  186. /* set the timezone and dst flag to the local rules. Also sets the
  187.    global variable tzname to the names of the timezones
  188.  */
  189.  
  190. char *tzname[2] = {"UCT", "UCT"};
  191.  
  192. void
  193. tzset()
  194. {
  195.     _timezone = tzoffset(getenv("TZ"), &dst);
  196. }
  197.  
  198. /*
  199.  * determine the difference, in seconds, between the given time zone
  200.  * and Greenwich Mean. As a side effect, the integer pointed to
  201.  * by hasdst is set to 1 if the given time zone follows daylight
  202.  * savings time, 0 if there is no DST.
  203.  *
  204.  * Time zones are given as strings of the form
  205.  * "[TZNAME][h][:m][TZDSTNAME]" where h:m gives the hours:minutes
  206.  * east of GMT for the timezone (if [:m] does not appear, 0 is assumed).
  207.  * If the final field, TZDSTNAME, appears, then the time zone follows
  208.  * daylight savings time.
  209.  *
  210.  * Example: EST5EDT would represent the N. American Eastern time zone
  211.  *          CST6CDT would represent the N. American Central time zone
  212.  *          NFLD3:30NFLD would represent Newfoundland time (one and a
  213.  *              half hours ahead of Eastern).
  214.  *          OZCST-9:30 would represent the Australian central time zone.
  215.  *              (which, so I hear, doesn't have DST).
  216.  *
  217.  * NOTE: support for daylight savings time is currently very bogus.
  218.  * It's probably best to do without, unless you live in North America.
  219.  *
  220.  */
  221. #define TZNAMLEN    8    /* max. length of time zone name */
  222.  
  223. static
  224. time_t 
  225. tzoffset(s, hasdst)
  226.         char *s;
  227.         int  *hasdst;
  228. {
  229.         time_t off = 0;
  230.         int x, sgn = 1;
  231.     static char stdname[TZNAMLEN+1], dstname[TZNAMLEN+1];
  232.     static char unknwn[4] = "???";
  233.  
  234.     char *n;
  235.  
  236.         *hasdst = -1;                   /* Assume unknown */
  237.         if (!s || !*s)
  238.                 return 0;               /* Assume GMT */
  239.  
  240.        *hasdst = 0;
  241.  
  242.     n = stdname;
  243.         while (*s && isalpha(*s)) {
  244.         *n++ = *s++;        /* skip name */
  245.     }
  246.     *n++ = 0;
  247.  
  248. /* now figure out the offset */
  249.  
  250.         x = 0;
  251.         if (*s == '-') {
  252.                 sgn = -1;
  253.                 s++;
  254.         }
  255.         while (isdigit(*s)) {
  256.                 x = 10 * x + toint(*s);
  257.                 s++;
  258.         }
  259.         off = x * SECS_PER_HOUR;
  260.         if (*s == ':') {
  261.                 x = 0;
  262.                 s++;
  263.                 while (isdigit(*s)) {
  264.                         x = 10 * x + toint(*s);
  265.                         s++;
  266.                 }
  267.             off += (x * SECS_PER_MIN);
  268.         }
  269.  
  270.     n = dstname;
  271.         if (isalpha(*s)) {
  272.                 *hasdst = 1;
  273.         while (*s && isalpha(*s)) *n++ = *s++;
  274.     }
  275.     *n++ = 0;
  276.  
  277.     if (stdname[0])
  278.         tzname[0] = stdname;
  279.     else
  280.         tzname[0] = unknwn;
  281.  
  282.     if (dstname[0])
  283.         tzname[1] = dstname;
  284.     else
  285.         tzname[1] = stdname;
  286.  
  287.         return sgn * off;
  288. }
  289.  
  290. /*
  291.  * Given a tm struct representing the local time, determine whether
  292.  * DST is currently in effect. This should only be
  293.  * called if it is known that the time zone indeed supports DST.
  294.  *
  295.  * FIXME: For now, assume that everyone follows the North American
  296.  *   time zone rules, all the time. This means daylight savings
  297.  *   time is assumed to be in effect from the first Sunday in April
  298.  *   to the last Sunday in October. Prior to 1987, the old rules
  299.  *   (last Sunday in April to last Sunday in Oct.) are used, even when
  300.  *   (as in 1974) they're not applicable. Sorry.
  301.  *
  302.  */
  303.  
  304. static
  305. int indst(s, t)
  306.     long s;
  307.         const struct tm *t;
  308. {
  309.     int wkday;
  310.  
  311.     if (t->tm_wday >= 0)
  312.         wkday = t->tm_wday;
  313.     else
  314.         wkday = ((s / SECS_PER_DAY) + 4) % 7;
  315.  
  316.         if (t->tm_mon == 3) {           /* April */
  317. /* before 1987, see if there's another sunday in the month */
  318.                 if (t->tm_year < 87 && t->tm_wday + 30 - t->tm_mday < 7)
  319.                         return 1;       /* no there isn't */
  320. /* after 1987, see if a sunday has happened yet */
  321.                 if (t->tm_wday - t->tm_mday < 0)
  322.                         return 1;       /* yep */
  323.                 return 0;
  324.         }
  325.         if (t->tm_mon == 9) {           /* October */
  326.                 if (t->tm_wday + 31 - t->tm_mday < 7)
  327.                         return 0;       /* there are no more sundays */
  328.                 return 1;
  329.         }
  330. /* Otherwise, see if it's a month between April and October exclusive */
  331.         return (t->tm_mon > 3 && t->tm_mon < 9);
  332. }
  333.  
  334.